package com.tsd.core.thread;

import com.tsd.core.datasource.DataSourceContextHolder;
import com.tsd.core.utils.ExceptionUtil;
import com.tsd.core.utils.ListUtil;
import com.tsd.core.datasource.SpringContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * @ClassName: ThreadPoolTaskUtil
 * @Description: TODO
 * @Author: Hillpool
 * @Date: 2022/7/28 15:14
 * @Version: 1.0
 */
public class ThreadPoolTaskUtil {

    public static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolTaskUtil.class);

    private static final String NAME_THREAD_POOL_TASK_EXECUTOR = "threadPoolTaskExecutor";
    private static final String NAME_SERVICE_JOB_TASK_EXECUTOR = "serviceJobTaskExecutor";
    //批量批次分割默认大小
    private static final Integer DEFAULT_SPLIT_SIZE = 100;

    public static ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
        return SpringContextUtil.getBean(NAME_THREAD_POOL_TASK_EXECUTOR, ThreadPoolTaskExecutor.class);
    }

    public static ThreadPoolTaskExecutor getServiceJobTaskExecutor() {
        return SpringContextUtil.getBean(NAME_SERVICE_JOB_TASK_EXECUTOR, ThreadPoolTaskExecutor.class);
    }

    /**
     * 回滚主线程
     */
    public static void rollbackMainThread() {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }

    public static <T> void submitList(List<FutureTask<T>> tasks) {
        ThreadPoolTaskExecutor executor = getServiceJobTaskExecutor();
        submitList(executor, tasks);
    }

    public static <T> void submitList(ThreadPoolTaskExecutor executor, List<FutureTask<T>> tasks) {
        for (FutureTask<T> task : tasks) {
            executor.submit(task);
        }
    }

    @SafeVarargs
    public static <T> void submitList(FutureTask<T>... tasks) {
        ThreadPoolTaskExecutor executor = getServiceJobTaskExecutor();
        submitList(executor, tasks);
    }

    @SafeVarargs
    public static <T> void submitList(ThreadPoolTaskExecutor executor, FutureTask<T>... tasks) {
        for (FutureTask<T> task : tasks) {
            executor.submit(task);
        }
    }

    /**
     * 按照默认的分批大小（100）进行分批执行查询；
     * 对于批量处理，由于SQL长度限制，所以列表的数量不能太大，从而引入对列表进行分批处理
     *
     * @param list
     * @param callback
     * @param <T>
     * @param <E>
     * @return
     */
    public static <T, E> List<T> queryInList4Split(List<E> list, ThreadPoolCallback<T, E> callback) {
        return queryInList4Split(list, DEFAULT_SPLIT_SIZE, callback);
    }

    /**
     * 指定的分批大小进行分批执行查询；
     * 对于批量处理，由于SQL长度限制，所以列表的数量不能太大，从而引入对列表进行分批处理
     *
     * @param list
     * @param splitSize
     * @param callback
     * @param <T>
     * @param <E>
     * @return
     */
    public static <T, E> List<T> queryInList4Split(List<E> list, int splitSize, ThreadPoolCallback<T, E> callback) {
        String dataSource = DataSourceContextHolder.getDataSourceType();
        List<List<E>> splitList = ListUtil.split(list, splitSize);
        List<FutureTask<List<T>>> taskList = new ArrayList<>();
        for (List<E> itemList : splitList) {
            Callable<List<T>> callable = () -> {
                DataSourceContextHolder.setDataSourceType(dataSource);
                return callback.action(itemList);
            };
            FutureTask<List<T>> task = new FutureTask<>(callable);
            taskList.add(task);
        }
        submitList(taskList);
        List<T> resultList = new ArrayList<>();
        for (FutureTask<List<T>> task : taskList) {
            try {
                resultList.addAll(task.get());
            } catch (Exception e) {
                LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
                rollbackMainThread();
            }
        }
        return resultList;
    }

    /**
     * 按照默认的分批大小（100）进行分批执行更新；
     * 对于批量处理，由于SQL长度限制，所以列表的数量不能太大，从而引入对列表进行分批处理
     *
     * @param list
     * @param isAsync
     * @param callback
     * @param <T>
     * @param <E>
     */
    public static <T, E> void executeInList4Split(List<E> list, boolean isAsync, ThreadPoolCall<T, E> callback) {
        executeInList4Split(list, isAsync, DEFAULT_SPLIT_SIZE, callback);
    }

    /**
     * 按照指定的分批大小进行分批执行更新；
     * 对于批量处理，由于SQL长度限制，所以列表的数量不能太大，从而引入对列表进行分批处理
     *
     * @param list
     * @param isAsync
     * @param splitSize
     * @param callback
     * @param <T>
     * @param <E>
     */
    public static <T, E> void executeInList4Split(List<E> list, boolean isAsync, int splitSize, ThreadPoolCall<T, E> callback) {
        String dataSource = DataSourceContextHolder.getDataSourceType();
        ThreadPoolTaskExecutor executor = null;
        if (isAsync) {
            executor = getServiceJobTaskExecutor();
        }
        List<List<E>> splitList = ListUtil.split(list, splitSize);
        for (List<E> itemList : splitList) {
            if (executor != null) {
                executor.execute(() -> {
                    DataSourceContextHolder.setDataSourceType(dataSource);
                    callback.action(itemList);
                });
                continue;
            }
            callback.action(itemList);
        }
    }

    /**
     * 循环执行
     *
     * @param list
     * @param isAsync
     * @param callback
     * @param <T>
     * @param <E>
     */
    public static <T, E> void executeInList(List<E> list, boolean isAsync, ThreadPoolCallOne<T, E> callback) {
        String dataSource = DataSourceContextHolder.getDataSourceType();
        ThreadPoolTaskExecutor executor = null;
        if (isAsync) {
            executor = getServiceJobTaskExecutor();
        }
        for (E data : list) {
            if (executor != null) {
                executor.execute(() -> {
                    DataSourceContextHolder.setDataSourceType(dataSource);
                    callback.action(data);
                });
                continue;
            }
            callback.action(data);
        }
    }

    public static void execute(Runnable task) {
        ThreadPoolTaskExecutor executor = getServiceJobTaskExecutor();
        executor.execute(task);
    }
}
